/*
Copyright © 2024 wjsmc
*/
package internal

import (
	"path/filepath"
	"strings"

	"github.com/AlecAivazis/survey/v2"
	"github.com/fatih/color"
	"github.com/spf13/cobra"
	"nodemessage.com/ruov-cli/rlog"
	"nodemessage.com/ruov-cli/tpl"
	"nodemessage.com/ruov-cli/types"
	"nodemessage.com/ruov-cli/utils"
)

// genCmd represents the gen command
var genCmd = &cobra.Command{
	Use:   "gen",
	Short: "数据库表模块生成工具",
	Long:  color.New(color.FgYellow).Sprintf("选择数据表,生成controller,service,mapper,domain结构代码"),
	Run: func(cmd *cobra.Command, args []string) {
		if !genController && !genService && !genServiceImpl && !genMapper && !genMapperXml && !genPo && !genVo && !genDto {
			setterGenAll()
		}
		// 校验是否为项目目录
		xmlConf := checkIsApiPath()
		// 读取logic模块pom文件
		pom := readLogicPom()
		// 准备生成信息
		mods, tabs, overwrite := readyGenInfo(xmlConf, pom)
		// 生成模块
		genTabTpl(xmlConf, mods, overwrite, tabs)
	},
}

func init() {
	genCmd.Flags().BoolVarP(&genController, "controller", "c", false, "生成controller")
	genCmd.Flags().BoolVarP(&genService, "service", "s", false, "生成service")
	genCmd.Flags().BoolVarP(&genServiceImpl, "serviceImpl", "i", false, "生成service impl")
	genCmd.Flags().BoolVarP(&genMapper, "mapper", "m", false, "生成mapper")
	genCmd.Flags().BoolVarP(&genMapperXml, "mapperXml", "x", false, "生成mapper xml")
	genCmd.Flags().BoolVarP(&genPo, "po", "p", false, "生成po")
	genCmd.Flags().BoolVarP(&genVo, "vo", "v", false, "生成vo")
	genCmd.Flags().BoolVarP(&genDto, "dto", "d", false, "生成dto")
}

func readyGenInfo(xmlConf types.Config, tplPom *types.TplLoginPom) (mods []string, tabs []types.TableInfo, overwrite bool) {
	// 获取数据库表
	tables, err := utils.DbGetDatabaseSchema(xmlConf.Database.Username,
		xmlConf.Database.Password,
		xmlConf.Database.Host,
		xmlConf.Database.Port,
		xmlConf.Database.DBName)
	if err != nil {
		rlog.Logger.Fatal("获取数据库结构失败:%v", err)
	}
	// 选择表
	tablesName := utils.CollectMap(tables, func(i types.TableInfo) string {
		return i.Name
	})
	questions := []*survey.Question{
		{
			Name: "moduleName",
			Prompt: &survey.MultiSelect{
				Message:  "选择模块:",
				Options:  tplPom.Modules.Module,
				PageSize: 15,
			},
			Validate: survey.ComposeValidators(survey.Required),
		},
		{
			Name: "tableName",
			Prompt: &survey.MultiSelect{
				Message:  "选择表:",
				Options:  tablesName,
				PageSize: 15,
			},
			Validate: survey.ComposeValidators(survey.Required),
		},
		{
			Name: "overwrite",
			Prompt: &survey.Confirm{
				Message: "覆盖生成?:",
				Default: false,
			},
			Validate: survey.ComposeValidators(survey.Required),
		},
	}
	answers := struct {
		ModuleName []string
		TableName  []string
		Overwrite  bool
	}{}
	err = survey.Ask(questions, &answers)
	if err != nil {
		rlog.Logger.Fatal("数据选择失败:%v", err)
	}
	for _, t := range answers.TableName {
		for _, table := range tables {
			if table.Name == t {
				tabs = append(tabs, table)
			}
		}
	}
	return answers.ModuleName, tabs, answers.Overwrite
}

func genTabTpl(xmlConf types.Config, mods []string, overwrite bool, tabs []types.TableInfo) {
	for _, tab := range tabs {
		tabCamelCase := utils.StrToUpCamelCase(tab.Name)
		dbTypes := utils.RemoveDuplicates(utils.CollectMap(tab.Columns, func(i types.ColumnInfo) string {
			return utils.StrConvertMySqlToJava(i.DataType)
		}))
		dbImport := utils.GetJavaImport(dbTypes...)
		tplArg := map[string]any{
			"Table":    tab,
			"DbImport": dbImport,
		}
		for _, mod := range mods {
			basePath := filepath.Join(logicDir, mod, "src", "main")
			javaDirItem := []string{basePath, "java"}
			javaDirItem = append(javaDirItem, strings.Split(xmlConf.BasePack, ".")...)
			modItem := strings.Split(strings.TrimLeft(mod, logicDir+"-"), "-")
			javaDirItem = append(javaDirItem, modItem...)

			javaDir := filepath.Join(javaDirItem...)
			mapperXmlDir := filepath.Join(basePath, "resources", "mapper")
			poDir := filepath.Join(javaDir, "domain", "po")
			voDir := filepath.Join(javaDir, "domain", "vo")
			dtoDir := filepath.Join(javaDir, "domain", "dto")
			mapperDir := filepath.Join(javaDir, "mapper")
			serviceDir := filepath.Join(javaDir, "service")
			serviceImplDir := filepath.Join(serviceDir, "impl")
			controllerDir := filepath.Join(javaDir, "controller")

			basePack := strings.Join(append([]string{xmlConf.BasePack}, modItem...), ".")
			tplArg["PackageName"] = basePack
			tplArg["Api"] = "/" + strings.Join(append(modItem, utils.StrToCamelCase(tab.Name)), "/")
			tplArg["PreAuth"] = strings.Join(append(modItem, utils.StrToCamelCase(tab.Name)), ":")
			exitsFileHandler(genPo, filepath.Join(poDir, tabCamelCase+"Po"+".java"), overwrite, "logic.domain.po.java.vm", tplArg, mod, tab.Name, "po")
			exitsFileHandler(genVo, filepath.Join(voDir, tabCamelCase+"Vo"+".java"), overwrite, "logic.domain.vo.java.vm", tplArg, mod, tab.Name, "vo")
			exitsFileHandler(genDto, filepath.Join(dtoDir, tabCamelCase+"Dto"+".java"), overwrite, "logic.domain.dto.java.vm", tplArg, mod, tab.Name, "dto")
			exitsFileHandler(genMapper, filepath.Join(mapperDir, tabCamelCase+"Mapper"+".java"), overwrite, "logic.mapper.java.vm", tplArg, mod, tab.Name, "mapper")
			exitsFileHandler(genMapperXml, filepath.Join(mapperXmlDir, tabCamelCase+"Mapper.xml"), overwrite, "logic.mapper.xml.java.vm", tplArg, mod, tab.Name, " mapper.xml")
			exitsFileHandler(genService, filepath.Join(serviceDir, "I"+tabCamelCase+"Service"+".java"), overwrite, "logic.service.java.vm", tplArg, mod, tab.Name, "service")
			exitsFileHandler(genServiceImpl, filepath.Join(serviceImplDir, tabCamelCase+"ServiceImpl"+".java"), overwrite, "logic.service.impl.java.vm", tplArg, mod, tab.Name, "service impl")
			exitsFileHandler(genController, filepath.Join(controllerDir, tabCamelCase+"Controller"+".java"), overwrite, "logic.controller.java.vm", tplArg, mod, tab.Name, "controller")
		}
	}
	rlog.Logger.Info("%s,完成啦!尽情开发吧!", strings.Join(mods, ","))
}

func exitsFileHandler(conditions bool, destPath string, overwrite bool, vm string, tplArg any, mod, tabName, genType string) {
	if !conditions {
		return
	}
	if !overwrite && utils.FilePathExists(destPath) {
		rlog.Logger.Warn("文件 %s 已存在", destPath)
		return
	}
	err := utils.TplGenerateFromTemplate(destPath, tpl.TemplateFS, vm, tplArg)
	if err != nil {
		rlog.Logger.Fatal("%s %s 生成 %s 失败:%v", mod, tabName, genType, err)
	}
}
